local skynet = require "skynet.manager"
local cjson = require "cjson"
local util  = require "util"
local utils = require "utils"
local reason = require "reason"
local game_def = require "game_def"
local constant = require "constant"
local server_config = dofile(skynet.getenv("server_config"))

local table_insert = table.insert
local table_remove = table.remove
local table_unpack = table.unpack
local string_format = string.format
local tonumber = tonumber
local pcall = pcall
local debug_traceback = debug.traceback

local BLACK_JOKER_NUMBER = 14
local RED_JOKER_NUMBER = 15

local math_random = math.random

local redis_pool
local sync_uids = {}

local CMD = {}
function CMD.start()
    local redis_conf = {
        host = server_config.redis_conf.host,
        port = tonumber(server_config.redis_conf.port),
        db = tonumber(server_config.redis_conf.db),
        auth = server_config.redis_conf.auth,
    }

    redis_pool = require("redis_pool").new(redis_conf)

    skynet.retpack(true)
end

local function make_card_id(sign,number)
    return sign * 100 + (number or 3)
end

local function make_player_act_key(uid)
    return "data_" .. uid
end

--------------------------------并发控制相关[begin]-------------------------------
local function sync_run_by_act_id(act_id,f,...)
    local waiting_list = sync_uids[act_id]
    print("sync_uids===========",act_id,tostring_r(waiting_list))
    if waiting_list == nil then
        sync_uids[act_id] = true
    else
        if waiting_list == true then
            waiting_list = {}
            sync_uids[act_id] = waiting_list
        end
        local co = coroutine.running()
        table_insert(waiting_list,co)
        skynet.wait(co) --等待唤醒
    end

    local conn = redis_pool:get_conn_from_pool()
    local ok,ret = xpcall(f,debug.traceback,act_id,conn,...)
    redis_pool:putback_to_pool(conn)
    if not ok then errlog(ret) end

    --再接着调度
    local waiting_list = sync_uids[act_id]
    if waiting_list == true or #waiting_list == 0 then
        sync_uids[act_id] = nil
        return
    end    

    local co = table_remove(waiting_list,1)
    skynet.wakeup(co)
end

-----------------------------------协议-----------------------------------------
local function init_activity_data(act_id,conn,key,expire_time,now)
    print("init_activity_data111111",key,expire_time)
    local data = conn:get(key)
    if data then --如果有数据检查是否过期要删掉
        data = cjson.decode(data)
        if data.expire_time < now then --数据过期
            data = nil
        end
    end

    if not data then
        data = {
            expire_time = expire_time,--过期时间
            week_begin_time =  now,
            store_coins = 0,--金币库存
            give_award  = false,
            player_data_map = {},--玩家活动数据
        }
    end

    print("init_activity_data2222",tostring_r(data))
    local data_str = cjson.encode(data)
    conn:set(key,data_str)

    skynet.retpack(true)
end

local function clear_week_data(act_id,conn,key,last_time,now_time)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    --清周玩家数据
    act_data.last_week_begin_time = last_time
    act_data.week_begin_time = now_time
    act_data.give_award = false
    for _,player_data in pairs(act_data.player_data_map) do
        player_data.score = 0
    end

    local data_str = cjson.encode(act_data)
    conn:set(key,data_str)

    skynet.retpack(true)
end

local function set_award_status(act_id,conn,key)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)
    act_data.give_award = true

    local data_str = cjson.encode(act_data)
    conn:set(key,data_str)

    skynet.retpack(true)
end

local function get_week_rank_time(act_id,conn,key,rank_type)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    if rank_type == game_def.ACT_RANK_TYPE_NEW_BIND_CUR then
        local rank_begin_time = act_data.week_begin_time
        local rank_end_time   = utils.getWeekZeroTime(rank_begin_time,true)
        return skynet.retpack(rank_begin_time,rank_end_time)
    elseif rank_type == game_def.ACT_RANK_TYPE_NEW_BIND_LAST then
        local rank_begin_time = act_data.last_week_begin_time or 0
        local rank_end_time   = utils.getWeekZeroTime(rank_begin_time,true)
        return skynet.retpack(rank_begin_time,rank_end_time)
    end

    skynet.retpack(false)
end

local function get_act_store(act_id,conn,key)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    local store_coins = act_data.store_coins or 0
    skynet.retpack(store_coins)
end

local function set_act_store(act_id,conn,key,coins)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)
    act_data.store_coins = coins

    local data_str = cjson.encode(act_data)
    conn:set(key,data_str)
    skynet.retpack(true)
end

local function get_player_activity_data(act_id,conn,key,uid)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    local player_key = make_player_act_key(uid)
    local player_data = act_data.player_data_map[player_key]
    if not player_data then
        return skynet.retpack(false)
    end

    skynet.retpack(player_data)
end

--绑定玩家
local function bind_player(act_id,conn,key,data,bind_add_count)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    local src_key  = make_player_act_key(data.src_uid)
    local dest_key = make_player_act_key(data.bind_uid)
    if not act_data.player_data_map[src_key] then
        act_data.player_data_map[src_key] = {}
    end
    if not act_data.player_data_map[dest_key] then
        act_data.player_data_map[dest_key] = {}
    end

    local src_data  = act_data.player_data_map[src_key]
    local dest_data = act_data.player_data_map[dest_key]
    if src_data.bind_uid then --已经绑定玩家了
        return skynet.retpack(false)
    end

    src_data.bind_uid = data.bind_uid
    --src_data.free_count = (src_data.free_count or 0) + bind_add_count
    src_data.bind_count = (src_data.bind_count or 0) + bind_add_count
    src_data.history_free_count = (src_data.history_free_count or 0) + bind_add_count

    dest_data.invite_number = (dest_data.invite_number or 0) + 1

    local ret_data = utils.shallow_copy(src_data)

    local data_str = cjson.encode(act_data)
    conn:set(key,data_str)

    skynet.retpack(ret_data)
end

local function pay_ticket(uid,cost,player_data)
    local pay_coins = 0
    if (player_data.bind_count or 0) > 0 then
        player_data.bind_count = player_data.bind_count - 1
        return true,pay_coins,true
    end

    if (player_data.free_count or 0) > 0 then
        player_data.free_count = player_data.free_count - 1
        return true,pay_coins
    end

    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler','reduce_coins',
    uid,cost,reason.NEW_BIND_LUCKY_DRWA)
    if not ok or not succ or not ret then
        return false
    end
    --通知大厅金币变化
    R().hallsvr{key=uid}:send('.msg_handler','toagent',uid,'notify_money_changed',{coins = ret.curr})

    return true,ret.chged
end

--双倍积分
local function is_double_time(conf)
    local day_time_str = utils.getTodayDayStr()
    local begin_time_str = day_time_str .. " " .. conf.ac_content.double_start_time
    local end_time_str = day_time_str .. " " .. conf.ac_content.double_end_time
    local double_begin_time = utils.get_time_by_string(begin_time_str)
    local double_end_time = utils.get_time_by_string(end_time_str)

    local now = util.get_now_time()
    if now >= double_begin_time and now <= double_end_time then
        return true
    end

    return false
end

--随机一个不中奖的牌
local function rand_noaward_card(card_rate)
    local ret_data = {}
    local card_list = {}
    for _,card_info in pairs(card_rate) do
        if card_info.award_coins <= 0 then
            table_insert(card_list,card_info)
        end
    end

    if #card_list > 0 then
        local index = math_random(1,#card_list)
        ret_data.card_num = card_list[index].card
        ret_data.award_score = card_list[index].award_score
        ret_data.award_coins = card_list[index].award_coins
    end

    return ret_data
end

local function random_card(store_coins,conf,use_bind,pay_coins)
    local ret_data = {}
    local add_rate = 0
    local random_num = math_random(1, 100)
    local card_rate = conf.ac_content.card_rate
    for _,card_info in pairs(card_rate) do
        add_rate = add_rate + card_info.rate
        if random_num <= add_rate then
            ret_data.card_num = card_info.card
            ret_data.award_score = card_info.award_score
            ret_data.award_coins = card_info.award_coins
            break
        end
    end

    --万一配置错了,随机不出牌,记录下日志,随机一个不中奖的牌
    if not next(ret_data) then
        errlog("random_card_error")
        ret_data = rand_noaward_card(conf.ac_content.card_rate)
    end

    --绑定的次数,需要充值达到一定金额才能中奖
    if use_bind and (pay_coins or 0) < conf.ac_content.bind_condition then
        ret_data = rand_noaward_card(conf.ac_content.card_rate)
        print("use_bind========",pay_coins)
    end

    --双倍积分
    ret_data.award_score = ret_data.award_score or 0
    ret_data.award_coins = ret_data.award_coins or 0
    if is_double_time(conf) then
        ret_data.award_score = ret_data.award_score*2
        ret_data.award_coins = ret_data.award_coins*2
    end
    
    --小于警报值,再随机一次不中奖牌
    if store_coins - ret_data.award_coins < conf.ac_content.store_warn then
        ret_data = rand_noaward_card(conf.ac_content.card_rate)
    end

    --随机一个牌颜色
    local num = ret_data.card_num
    if num ~= BLACK_JOKER_NUMBER and num ~= RED_JOKER_NUMBER then
        local sign = math_random(1,4)
        ret_data.card_num = make_card_id(sign,num)
    end

    return ret_data
end

--玩家抽奖
local function new_bind_lucky_draw(act_id,conn,key,uid,conf)
    local act_data = conn:get(key)
    if not act_data then
        return skynet.retpack(false)
    end
    act_data = cjson.decode(act_data)

    local player_key  = make_player_act_key(uid)
    local player_data = act_data.player_data_map[player_key]
    if not player_data then
        return skynet.retpack(false)
    end

    --免费次数不够时,检查金币
    local award_cost = conf.ac_content.award_cost or 1000
    local free_count = (player_data.free_count or 0) + (player_data.bind_count or 0)
    print("new_bind_lucky_draw_free_count====",free_count)
    if free_count <= 0 then
        local ok,curr_coins = R().basesvr({key=uid}):call('.msg_handler','get_coins',uid)
        if not ok or (curr_coins or 0) < award_cost then
            return skynet.retpack(false)
        end
    end

    --扣钱加进库存
    local pay_sucess,pay_coins,use_bind = pay_ticket(uid,award_cost,player_data)
    if not pay_sucess then
        return skynet.retpack(false)
    end
    act_data.store_coins = (act_data.store_coins or 0) + pay_coins

    --抽奖次数增加,检查是否触发绑定的玩家免费次数增加
    player_data.award_count = (player_data.award_count or 0) + 1
    if player_data.award_count % conf.ac_content.advert_condition == 0 then --达到推广目标
        local bind_key = make_player_act_key(player_data.bind_uid)
        local bind_player_data = act_data.player_data_map[bind_key]
        if bind_player_data then
            if not bind_player_data.free_count or bind_player_data.free_count == 0 then
                R().hallsvr({key=uid}):send('.msg_handler','toagent',uid,'notify_red_points',constant.MODULE_DEF.PUKEWANG)
            end
            bind_player_data.free_count = (bind_player_data.free_count or 0) + 1
            bind_player_data.history_free_count = (bind_player_data.history_free_count or 0) + 1
        end
    end

    --随机一个牌,判断中奖,发放奖励
    local ok,pay_money = R().basesvr({key=uid}):call('.msg_handler','get_pay_money',uid)
    if not ok then
        errlog("get_pay_money======")
    end

    local card_data = random_card(act_data.store_coins,conf,use_bind,pay_money)
    player_data.score = (player_data.score or 0) + card_data.award_score
    act_data.store_coins = (act_data.store_coins or 0) - card_data.award_coins

    local ret_data = {
        card_data = card_data,
        pay_coins = pay_coins,
        player_data = utils.shallow_copy(player_data),
    }

    local data_str = cjson.encode(act_data)
    conn:set(key,data_str)

    skynet.retpack(ret_data)
end

---------------------------------------------------------------------------
function CMD.init_activity_data(act_id,...) sync_run_by_act_id(act_id,init_activity_data,...) end
function CMD.get_player_activity_data(act_id,...) sync_run_by_act_id(act_id,get_player_activity_data,...) end
function CMD.bind_player(act_id,...) sync_run_by_act_id(act_id,bind_player,...) end
function CMD.new_bind_lucky_draw(act_id,...) sync_run_by_act_id(act_id,new_bind_lucky_draw,...) end
function CMD.clear_week_data(act_id,...) sync_run_by_act_id(act_id,clear_week_data,...) end
function CMD.set_award_status(act_id,...) sync_run_by_act_id(act_id,set_award_status,...) end
function CMD.get_week_rank_time(act_id,...) sync_run_by_act_id(act_id,get_week_rank_time,...) end
function CMD.get_act_store(act_id,...) sync_run_by_act_id(act_id,get_act_store,...) end
function CMD.set_act_store(act_id,...) sync_run_by_act_id(act_id,set_act_store,...) end
---------------------------------------------------------------------------

skynet.start(function()
    skynet.dispatch("lua",function(_,_,action,...)
        print('============act_data_interce got params==============......',action,...)
        CMD[action](...)
    end)
end)